﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Principal;
using System.Text.RegularExpressions;
using System.Reflection;
using System.Diagnostics;
using System.Security;
using System.ComponentModel;
using System.Collections;
using System.Linq.Expressions;
using System.Xml.Serialization;
using System.Xml;

namespace Idlesoft.Utils
{
	public static class Common
	{
		#region consts
		public const double BYTES_PER_MB = 1048576;
		public const string XML_EXT = ".xml";

		#endregion consts

		#region props
		public static char[] atoz { get { return Enumerable.Range('a', 'z' - 'a' + 1).Select(i => (char)i).ToArray(); } }
		public static char[] ATOZ { get { return Enumerable.Range('A', 'Z' - 'A' + 1).Select(i => (char)i).ToArray(); } }
		#endregion props

		#region methods

		public static string Check<T>(Func<T> expr)
		{
			// get IL code behind the delegate
			var il = expr.Method.GetMethodBody().GetILAsByteArray();
			// bytes 2-6 represent the field handle
			var fieldHandle = BitConverter.ToInt32(il, 2);
			// resolve the handle
			var field = expr.Target.GetType().Module.ResolveField(fieldHandle);

			// get value
			var e = expr();

			string value = e.ToString();
			//if (e.GetType().IsType(typeof(IEnumerable)))
			//{
			//    (e as IEnumerable).ForEach(
			//}
				//value = (e as IEnumerable).Fold( (acc, curr)=> acc + curr, "");

			return string.Format("Name={0} Value={1}", field.Name, value);
		}

		public static string GetEmbeddedResource<TItem>(this TItem item, string resource)
			where TItem : class
		{
			string text = null;
			try
			{
				var assembly = Assembly.GetAssembly(typeof(TItem));
				using (Stream stream = assembly.GetManifestResourceStream(resource))
				{
					if (stream == null)
						throw new Exception("Embedded resource " + resource + " doesn't exists");
					byte[] bytes = new byte[stream.Length];
					stream.Position = 0;
					stream.Read(bytes, 0, (int)stream.Length);
					text = Encoding.ASCII.GetString(bytes); // this is your string
				}
			}
			catch (Exception ex)
			{
				throw new Exception(ex.Message);
			}
			return text;
		}

		public static int Depth(Type type)
		{
			return (type == typeof(object)) ? 0 : Depth(type.BaseType);
		}
		public static string GetVariableName<T>(Expression<Func<T>> expr)
		{
			return (expr.Body as MemberExpression).Member.Name;
		}

		public static string FormatDateTimeForOs(DateTime dt) { return Regex.Replace(dt.ToString(), @"[/:]", "."); }
		public static string EnumToString(Enum e)
		{
			return (e != null) ? e.GetType().FullName + "." + Enum.GetName(e.GetType(), e) : null;
		}
		public static Enum EnumFromString(string s)
		{
			Enum result = null;
			Match match = Regex.Match(s, @"(.+)(\.)(.+)");

			if (match.Groups.Count == 4)
			{
				string type = match.Groups[1].Value;
				string value = match.Groups[3].Value;
				Type enumType = Assembly.GetCallingAssembly().GetType(type);
				foreach (Enum v in Enum.GetValues(enumType))
				{
					if (EnumToString(v) == type + "." + v)
					{
						result = v;
						break;
					}
				}
			}
			return result;
		}

		public static string ExpandEnvironmentVariablePath(string file)
		{
			var filename = Path.GetFileName(file);
			var paths = Environment.GetEnvironmentVariable("PATH").Split(';');

			if (!File.Exists(file))
			{
				foreach (var path in paths)
				{
					if (Directory.Exists(path))
					{
						var files = Directory.GetFiles(path);
						foreach (var f in files)
						{
							if (Path.GetFileNameWithoutExtension(f).Equals(filename) || Path.GetFileName(f).Equals(filename))
							{
								filename = Path.GetFullPath(Path.Combine(path, f));
								break;
							}
						}
					}
				}
			}
			if (file == filename)
			{
				var guidfile = Guid.NewGuid() + ".txt";
				File.WriteAllText(Path.Combine("C:\temp", guidfile), paths.Flatten("\n"));
			}

			return filename;
		}
		public static int RunProcess(string file, string args, string domain, string username, SecureString password, Action<object, DataReceivedEventArgs> stdout = null, Action<object, DataReceivedEventArgs> stderr = null)
		{
			int exitcode = unchecked((int)0xDEADBEEF);

			var filename = ExpandEnvironmentVariablePath(file);
			var filepath = Path.GetFullPath(file);

			var psi = new ProcessStartInfo(filename, args);
			psi.WorkingDirectory = Path.GetDirectoryName(filepath);
			psi.CreateNoWindow = true;
			psi.RedirectStandardOutput = true;
			psi.RedirectStandardError = true;
			psi.UseShellExecute = false;
			psi.Domain = !domain.IsNullOrEmpty() ? domain : null;
			psi.UserName = !username.IsNullOrEmpty() ? username : null;
			psi.Password = password != null ? password : null;

			try
			{
				using (Process process = new Process())
				{
					process.StartInfo = psi;
					process.EnableRaisingEvents = true;

					if (System.Diagnostics.Debugger.IsAttached)
					{
						process.OutputDataReceived += (s,e) => Debug.WriteLine("stdout=" + e.Data);
						process.ErrorDataReceived += (s,e) => Debug.WriteLine("stderr=" + e.Data);
					}
					if (stdout != null) process.OutputDataReceived += new DataReceivedEventHandler(stdout);
					if (stderr != null) process.ErrorDataReceived += new DataReceivedEventHandler(stderr);

					process.Start();
					process.BeginOutputReadLine();
					process.BeginErrorReadLine();
					process.WaitForExit();
					exitcode = process.ExitCode;
				}
			}
			catch (Exception ex)
			{
				ex.Print();
				throw ex;
			}
			return exitcode;
		}
		public static int RunProcess(string path, string args, Action<object, DataReceivedEventArgs> stdout = null, Action<object, DataReceivedEventArgs> stderr = null)
		{
			return RunProcess(path, args, null, null, null, stdout, stderr);
		}
		public static string BitView(int value)
		{
			string view = "";
			for (int i = 31; i >= 0; --i)
				view += ((value & (1 << i)) != 0) ? '1' : '0';
			return view;
		}
		public static int[] ExtractFlags(int flags)
		{
			List<int> list = new List<int>();

			for (int i = 31; i >= 0 && flags != 0; --i)
			{
				int mask = 1 << i;
				if ((mask & flags) != 0)
				{
					list.Add(mask);
					flags ^= mask;
				}
			}
			return list.ToArray();
		}
		public static object InvokeGetMember(MemberInfo member, object target)
		{
			object value = null;
			switch (member.MemberType)
			{
				case MemberTypes.Property: value = InvokeGetMember((member as PropertyInfo).GetGetMethod(), target); break;
				case MemberTypes.Field: value = (member as FieldInfo).GetValue(target); break;
				case MemberTypes.Method: value = (member as MethodInfo).Invoke(target, null); break;
				default: throw new Exception("wtf");
			}
			return value;
		}
		public static void InvokeSetMember(MemberInfo member, object target, params object[] values)
		{
			object array = ConvertToMemberTypes(member, values);
			switch (member.MemberType)
			{
				case MemberTypes.Property: (member as PropertyInfo).GetSetMethod().Invoke(target, new object[] { array }); break;
				case MemberTypes.Field: (member as FieldInfo).SetValue(target, array as object[]); break;
				case MemberTypes.Method: (member as MethodInfo).Invoke(target, array as object[]); break;
				default: throw new Exception("wtf");
			}
		}
		public static object ConvertToMemberTypes(MemberInfo member, params object[] values)
		{
			object converted = null;
			switch (member.MemberType)
			{
				case MemberTypes.Property: converted = ConvertToType((member as PropertyInfo).PropertyType, values); break;
				case MemberTypes.Field: converted = ConvertToType((member as FieldInfo).FieldType, values); break;
				case MemberTypes.Method:
					Type[] types = (member as MethodInfo).GetParameters().Select(p => p.ParameterType).ToArray();
					if (types.Count(t => t.IsArray) > 1)
						throw new Exception("wtf");
					converted = ZipTypesAndValues(types, values);
					break;
				default: throw new Exception("wtf");
			}
			return converted;
		}
		public static object[] ZipTypesAndValues(Type[] types, object[] values)
		{
			List<object> converted = new List<object>();

			for (int i = 0; i < types.Length; ++i)
			{
				Type type = types[i];
				var value = values[i];

				if (type.IsArray)
				{
					if (i == types.Length - 1)
					{
						converted.Add(ConvertToType(type, values));
					}
					else
					{
						var result = ZipTypesAndValues(types.Skip(i).Reverse().ToArray(), values.Skip(i).Reverse().ToArray());
						converted.AddRange(result.Reverse());
					}
				}
				else
				{
					converted.Add(ConvertToType(type, value));
				}
			}

			return converted.ToArray();
		}
		public static object ConvertToType(Type type, params object[] values)
		{
			object converted = null;
			switch (values.Length)
			{
				case 0: throw new Exception("wtf");
				case 1: converted = Convert(type, values.First()); break;
				default: converted = Convert(type, values); break;
			}
			return converted;
		}
		private static object Convert(Type type, object obj)
		{
			TypeConverter converter = GetConverter(type);
			object converted = obj;
			if (!type.Equals(obj.GetType()))
				converted = converter.ConvertFrom(obj);
			return converted;
		}

		public static TypeConverter GetConverter(Type type)
		{
			TypeConverter converter = null;
			ConstructorInfo ctor = null;
			Type[] types = new Type[] { type.GetElementType() };

			if (type.IsArray)
			{
				ctor = typeof(ArrayConverter<>).MakeGenericType(types).GetConstructor(Type.EmptyTypes);
				converter = ctor.Invoke(null) as TypeConverter;
			}
			else if (type.IsGenericType)
			{
				if (type.Equals(typeof(Nullable<>)))
				{
					converter = new NullableConverter(type);
				}
				else if (type.Equals(typeof(List<>)))
				{
					ctor = typeof(ListConverter<>).MakeGenericType(types).GetConstructor(Type.EmptyTypes);
					converter = ctor.Invoke(null) as TypeConverter;
				}
				else if (type.Equals(typeof(Dictionary<,>)))
				{
					ctor = typeof(DictionaryConverter<,>).MakeGenericType(types).GetConstructor(Type.EmptyTypes);
					converter = ctor.Invoke(null) as TypeConverter;
				}
			}
			else
				converter = TypeDescriptor.GetConverter(type);

			return converter;
		}

		public static bool ParseArray<T>(object input, ref object output)
		{
			bool result = false;
			output = new string[] { input as string };
			ArrayList list = new ArrayList();

			// Searches for 1D arrays in one 2D array  ex:{{a,b,c},{d,e}} ==> {a,b,c} and {d,e}
			MatchCollection collection = Regex.Matches(input as string, "{[^{]+[^}]}");

			for (int i = 0; i < collection.Count; i++)
			{
				Match match = Regex.Match(collection[i].ToString(), @"(\{ *)(.*)( *\})");
				string s = input as string;

				string[] inValues = default(string[]);
				T[] outValues = default(T[]);
				if (match.Success && match.Groups.Count == 4)
				{
					string array = Regex.Replace(match.Groups[2].Value, "^ +| +$", "");
					inValues = Regex.Split(array, ", *");
					outValues = new T[inValues.Length];
				}
				else
				{
					inValues = new string[] { input as string };
					outValues = new T[1] { default(T) };
				}
				if (inValues != default(string[]) && outValues != default(T[]))
				{
					Type type = typeof(T);
					TypeConverter converter = null;

					// Currently, ini supports till 2D array. Processing of 1D and 2D arrays are handled seperately
					// Check for type and get rank if it's an array
					if (type.IsArray && type.GetArrayRank() > 0)
					{
						// if 2D array
						converter = TypeDescriptor.GetConverter(type.GetElementType());
						if (converter != null)
						{
							list.Add(inValues);
							output = (T[])list.ToArray(typeof(T));
						}
					}
					else
					{
						// if 1D array or single value
						converter = TypeDescriptor.GetConverter(type);
						if (converter != null)
						{
							for (int index = 0; index < inValues.Length; ++index)
								outValues[index] = (T)converter.ConvertFromInvariantString(inValues[index] as string);

							output = outValues;
						}
					}
				}
			}

			return result;
		}
		public static bool ParseArray<K, V>(object input, ref object output)
		{
			bool result = false;
			output = input;
			Match match = Regex.Match(input as string, @"(\{ *)(.*)( *\})");
			if (match.Success)
			{
				throw new NotImplementedException("dictionary support to come");
			}
			return result;
		}

		public static string GetWorkWeek(DateTime dt)
		{
			// Intel logic for WW is:
			//	The week containing January 1st is always WW01. If the current date is in 
			//	the last week of December and that week contains Jan 1, then the WW is 01 
			//	of the following year. Years where Dec 31 falls on Saturday will have a WW53.

			string workweek = "";

			int currentYear = dt.Year;
			int nextYear = currentYear + 1;

			// Get first date (Sunday) of current week
			DateTime firstDateOfCurrentWeek = dt.AddDays(0 - (double)dt.DayOfWeek);

			// Get Jan 1 of following year
			DateTime janFirstNextYear = DateTime.Parse("01-JAN-" + nextYear.ToString());

			// If current week contains Jan 1 of next year
			if (janFirstNextYear.Subtract(firstDateOfCurrentWeek).Days < 7)
			{
				// Current week is WW01 of next year
				workweek = (nextYear * 100 + 1).ToString();
			}
			else
			{
				// Get Jan 1 of current year
				DateTime janFirstThisYear = DateTime.Parse("01-JAN-" + currentYear.ToString());

				// Get date of Sunday WW01 this year (DayOfWeek.Sunday is 0)
				int offset = (int)janFirstThisYear.DayOfWeek;
				DateTime firstDay = janFirstThisYear.AddDays(0 - (double)offset);

				// Get number of days elapsed since WW01.0
				TimeSpan daysElapsed = dt.Subtract(firstDay);

				// Divide by 7, round down, and add 1 to get WW
				int ww = daysElapsed.Days / 7 + 1;

				// Build string. Pad WW if needed.
				workweek = (currentYear * 100 + ww).ToString();
			}

			return workweek;
		}
		public static string StringToRegexPattern(string str) { return Regex.Replace(str, @"\\", @"\\"); }
		public static string WildcardToRegex(string pattern)
		{
			// From http://www.codeproject.com/KB/recipes/wildcardtoregex.aspx
			return "^" + Regex.Escape(pattern).Replace("\\*", ".*").Replace("\\?", ".") + "$";
		}

		#region find files
		public static string[] FindFiles(string src, SearchOption option, bool filenameOnly, params string[] patterns)
		{
			return FindFiles(src, option, -1.0, null, filenameOnly, patterns);
		}
		public static string[] FindFiles(string src, SearchOption option, double maxFileSize, bool filenameOnly, params string[] patterns)
		{
			return FindFiles(src, option, maxFileSize, null, filenameOnly, patterns);
		}
		public static string[] FindFiles(string src, SearchOption option, string[] exclusions, bool filenameOnly, params string[] patterns)
		{
			return FindFiles(src, option, -1.0, exclusions, filenameOnly, patterns);
		}
		public static string[] FindFiles(string src, SearchOption option, double maxFileSize, string[] exclusions, bool filenameOnly, params string[] patterns)
		{
			if (patterns.IsNullOrEmpty())
				throw new ArgumentException("Common.FindFiles: string[] patterns cannot be null or empty");

			List<string> files = new List<string>();
			try
			{
				//make sure source directory exists
				if (!Directory.Exists(src))
					return null;

				foreach (string pattern in patterns)
				{
					string[] srcFiles = Directory.GetFiles(src, pattern, option).
						Where(s =>
						{
							//dont include files that match an exclusion
							if (exclusions != null)
							{
								foreach (string ex in exclusions)
									if (Regex.IsMatch(s, Common.StringToRegexPattern(ex))) return false;
							}
							//dont include files that are too big
							if (maxFileSize > 0)
							{
								double fileSize = GetFileSizeInMB(s);
								if (fileSize > maxFileSize)
									return false;
							}
							return true;
						}).ToArray();

					// if we want the file name only- strip the path
					if (filenameOnly)
					{
						foreach (string srcFile in srcFiles)
							files.Add(Path.GetFileName(srcFile));
					}
					else
						files.AddRange(srcFiles);
				}
			}
			catch (Exception ex)
			{
				ex.Print();
			}

			return files.ToArray();
		}
		public static double GetFileSizeInMB(string filename)
		{
			double totalBytes = 0;
			
			FileInfo fi = new FileInfo(filename);
			totalBytes += fi.Length;
			// Calc size in megabytes
			double megaBytes = (double)(totalBytes / BYTES_PER_MB);
			// Round to three digits
			megaBytes = Math.Round(megaBytes, 3);
			return megaBytes;
		}
		#endregion find files

		#region copy files
		public static string[] CopyFiles(string src, string dst, SearchOption option, params string[] patterns)
		{
			return CopyFiles(src, dst, null, option, -1.0, null, patterns);
		}
		public static string[] CopyFiles(string src, string dst, string rename, SearchOption option, params string[] patterns)
		{
			return CopyFiles(src, dst, rename, option, -1.0, null, patterns);
		}
		public static string[] CopyFiles(string src, string dst, string rename, SearchOption option, double maxFileSize, params string[] patterns)
		{
			return CopyFiles(src, dst, rename, option, maxFileSize, null, patterns);
		}
		public static string[] CopyFiles(string src, string dst, SearchOption option, string[] exclusions, params string[] patterns)
		{
			return CopyFiles(src, dst, null, option, -1.0, exclusions, patterns);
		}
		public static string[] CopyFiles(string src, string dst, string rename, SearchOption option, double maxFileSize, string[] exclusions, params string[] patterns)
		{
			List<string> files = new List<string>();
			try
			{
				//get all of the files to copy but make sure it is not the name of the entry assembly...
				string[] srcFiles = FindFiles(src, option, maxFileSize, exclusions, false, patterns).
					Where(f => { return Path.GetFileNameWithoutExtension(f) != Assembly.GetEntryAssembly().GetName().Name; }).ToArray();

				foreach (string srcFile in srcFiles)
				{
					//look for any unique parts of the path to preserve the file structure
					string dstFile = dst;
					if (option == SearchOption.AllDirectories)
					{
						string a = Common.StringToRegexPattern(src);
						string z = Common.StringToRegexPattern(Path.GetFileName(srcFile));
						string unique = Regex.Replace(srcFile, @"\A" + a + "|" + z + @"\Z", "");
						dstFile = Path.Combine(dst, unique);
					}

					//create the final destination directory if it doesnt exist
					if (!Directory.Exists(dstFile))
						Directory.CreateDirectory(dstFile);

					//if the rename option is selected use the UniqueName which is the UniqueId (required) and UniqueTimeStamp (generated)						
					if (!string.IsNullOrEmpty(rename))
					{
						string insert = "";
						if (srcFiles.Length > 1)
							insert = "." + Path.GetFileNameWithoutExtension(srcFile);
						dstFile = Path.Combine(dstFile, rename + insert + Path.GetExtension(srcFile));
					}
					else
						dstFile = Path.Combine(dstFile, Path.GetFileName(srcFile));

					//change any .wtl files to .xml files
					if (Path.GetExtension(srcFile) == ".wtl")
						Path.ChangeExtension(dstFile, ".wtl.xml");

					string s = Path.GetFullPath(srcFile);
					string d = Path.GetFullPath(dstFile);
					string sd = Path.GetDirectoryName(s);
					string dd = Path.GetDirectoryName(d);

					//copy the file
					try
					{
						File.Copy(srcFile, dstFile, true);
					}
					catch (Exception ex)
					{
						ex.Print();
					}

					//record the failures
					if (!File.Exists(dstFile))
					{
						//Logger.ForAll.
						//WriteLine("Failed to copy: ").
						//WriteLine("src: " + srcFile + " to").
						//WriteLine("dst: " + dstFile);
					}
					else
						files.Add(dstFile);
				}
			}
			catch (Exception ex)
			{
				ex.Print();
			}

			return files.ToArray();
		}
		#endregion copy files

		#region move files
		public static string[] MoveFiles(string src, string dst, SearchOption option, params string[] patterns)
		{
			return MoveFiles(src, dst, null, option, -1.0, null, patterns);
		}
		public static string[] MoveFiles(string src, string dst, string rename, SearchOption option, params string[] patterns)
		{
			return MoveFiles(src, dst, rename, option, -1.0, null, patterns);
		}
		public static string[] MoveFiles(string src, string dst, string rename, SearchOption option, double maxFileSize, params string[] patterns)
		{
			return MoveFiles(src, dst, rename, option, maxFileSize, null, patterns);
		}
		public static string[] MoveFiles(string src, string dst, SearchOption option, string[] exclusions, params string[] patterns)
		{
			return MoveFiles(src, dst, null, option, -1.0, exclusions, patterns);
		}
		public static string[] MoveFiles(string src, string dst, string rename, SearchOption option, double maxFileSize, string[] exclusions, params string[] patterns)
		{
			string[] files = CopyFiles(src, dst, rename, option, maxFileSize, exclusions, patterns);

			string[] deleted = DeleteFiles(src, option, patterns);

			//foreach (string file in files)
			//{
			//    string top = Regex.Replace(file, Common.StringToRegexPattern(dst), "");
			//    string del = Common.PathCombine(src, top);
			//    if (File.Exists(del))
			//        File.Delete(del);
			//}
			return files;
		}
		#endregion move files

		#region delete files
		public static string[] DeleteFiles(string src, SearchOption option, params string[] patterns)
		{
			List<string> files = new List<string>();

			foreach (string pattern in patterns)
			{
				string[] delFiles = Directory.GetFiles(src, pattern, option);
				foreach (string delFile in delFiles)
				{
					if (File.Exists(delFile))
						File.Delete(delFile);
				}
			}

			return files.ToArray();
		}
		#endregion delete files

		#region class property reflection
		public static bool AssertNx2(object[][] targetectArrayNx2)
		{
			if (targetectArrayNx2.IsNullOrEmpty())
				return false;

			if (targetectArrayNx2.GetLength(1) != 2)
				return false;

			return true;
		}
		public static bool Assert2xN(object[][] targetectArray2xN)
		{
			if (targetectArray2xN.IsNullOrEmpty())
				return false;

			if (targetectArray2xN.GetLength(0) != 2)
				return false;

			return true;
		}

		public static PropertyInfo[] GetProperties(object target, Func<PropertyInfo, bool> predicate)
		{
			PropertyInfo[] pis = ((IEnumerable<PropertyInfo>)target.GetType().GetProperties().Where(predicate)).ToArray();
			return pis;
		}
		public static FieldInfo[] GetFields(object target, Func<FieldInfo, bool> predicate)
		{
			FieldInfo[] fis = ((IEnumerable<FieldInfo>)target.GetType().GetFields().Where(predicate)).ToArray();
			return fis;
		}

		public static KeyValuePair<string, object>[] OutputKeyValuePairs(object target, Func<PropertyInfo, bool> predicate)
		{
			List<KeyValuePair<string, object>> kvps = new List<KeyValuePair<string, object>>();
			object[][] pairs = OutputObjectArraysNx2(target, predicate);
			foreach (object[] pair in pairs)
				kvps.Add(new KeyValuePair<string, object>(pair[0] as string, pair[1]));
			return kvps.ToArray();
		}
		public static object[][] OutputObjectArraysNx2(object target, Func<PropertyInfo, bool> predicate)
		{
			object[][] arrays = null;

			string[] names = GetPropertyNames(target, predicate);
			object[] values = GetPropertyValues(target, predicate);
			arrays = new object[names.Length][];

			for (int index = 0; index < names.Length; ++index)
				arrays[index] = new object[] { names[index], values[index] };

			return arrays;
		}
		public static object[][] OutputObjectArrays2xN(object target, Func<PropertyInfo, bool> predicate)
		{
			object[][] arrays = null;

			string[] names = GetPropertyNames(target, predicate);
			object[] values = GetPropertyValues(target, predicate);
			arrays = new object[2][] { null, null };

			arrays[0] = names;
			arrays[1] = values;

			return arrays;
		}

		public static string[] GetPropertyNames(object target, Func<PropertyInfo, bool> predicate)
		{
			PropertyInfo[] pis = GetProperties(target, predicate);
			List<string> names = new List<string>();
			foreach (PropertyInfo pi in pis) { names.Add(pi.Name); }
			return names.ToArray();
		}
		public static object[] GetPropertyValues(object target, Func<PropertyInfo, bool> predicate)
		{
			PropertyInfo[] pis = GetProperties(target, predicate);
			List<object> values = new List<object>();
			foreach (PropertyInfo pi in pis) { values.Add(pi.GetValue(target, new object[] { })); }
			return values.ToArray();
		}

		public static object Clone(object source, object target)
		{
			var matches = from src in source.GetType().GetProperties()
						  join tgt in target.GetType().GetProperties()
						  on new { src.Name, src.PropertyType } 
						  equals new { tgt.Name, tgt.PropertyType }
						  select new
						  {
							  Source = src,
							  Target = tgt
						  };
			foreach (var match in matches)
			{
				object value = match.Source.GetValue(source, null);
				match.Target.SetValue(target, value, null);
			}
			return target;
		}
		public static object CloneTo(this object source, object target)
		{
			return Clone(source, target);
		}
		public static object CloneFrom(this object target, object source)
		{
			return Clone(source, target);
		}
		#endregion class property reflection

		#endregion methods
	}

	/// <summary>
	/// Comparer for putting files in order by CreationTime...
	/// </summary>
	public class FileCreationDateComparer : IComparer<string>
	{
		#region ctors
		public FileCreationDateComparer()
		{
		}
		#endregion ctors

		#region methods
		public int Compare(string file1, string file2)
		{
			int result = 0;
			string msg = "";
			try
			{
				FileInfo fi1 = new FileInfo(file1);
				FileInfo fi2 = new FileInfo(file2);

				DateTime dt1 = fi1.CreationTime;
				DateTime dt2 = fi2.CreationTime;


				msg = "DateTime1: " + fi1.CreationTime + " DateTime2: " + fi2.CreationTime + ">> ";
				result = DateTime.Compare(dt1, dt2);
			}
			catch (Exception ex) { /*Logger.ForVerbose.Log(ex);*/ throw ex; }
			return result;
		}
		#endregion methods
	}
}
